package com.study.springsecurity.security.filter;

/**
 * @Author: zzy
 * @Date: 2024/10/12 16:19
 * @Title:
 * @Version:
 * @Package: com.study.springsecurity.security
 */

import cn.hutool.jwt.JWTUtil;
import com.study.springsecurity.security.user.DBUserDetailsManager;
import com.study.springsecurity.utils.MyConstant;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 过滤器，每次请求都会触发
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    private final static String AUTH_HEADER = "Authorization";
    private final static String AUTH_HEADER_TYPE = "Bearer";
    @Autowired
    private DBUserDetailsManager userDetailsService;

    // has token, save the token into context
    // doesn't have token, due to doesn't authentication can't be passed next filters
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // get token from header:  Authorization: Bearer <token>
        log.info("filter: {}", request.getRequestURL());
        String authHeader = request.getHeader(AUTH_HEADER);

        if (Objects.isNull(authHeader) || !authHeader.startsWith(AUTH_HEADER_TYPE)){
            // to next filter
            log.warn("token none, to next filter");
            filterChain.doFilter(request,response);
            return;
        }
        // get detail token expect the header
        String authToken = authHeader.split(" ")[1];
        log.info("authToken:{}" , authToken);
        // verify token
        if (!JWTUtil.verify(authToken, MyConstant.JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8))) {
            log.info("invalid token");
            filterChain.doFilter(request,response);
            return;
        }

        final String userName = (String) JWTUtil.parseToken(authToken).getPayload("username");
        UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
        log.info("userDetails: {}", userDetails.toString());
        // save the token into context, by this way can be passed next filters
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        log.info("authentication: {}", authentication);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        filterChain.doFilter(request, response);
    }
}